package edu.cse5311.impl;

/**
 * @author Karthik MG/ Shwetha Narayan
 * This class parses the input file containing the various system and transactional operations
 * and delegates the control to the corresponding operation handler
 */

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

import edu.cse5311.constants.Constants;
import edu.cse5311.entity.InputLine;
import edu.cse5311.entity.Item;
import edu.cse5311.entity.Operation;
import edu.cse5311.entity.Transaction;
import edu.cse5311.operations.SystemOperations;
import edu.cse5311.operations.TransactionOperations;

public class OperationDelegator {

	/**
	 * This function takes in the input file specified in the file path as input and reads it line after line
	 * and delegates the control to the operation handler 
	 */
	public void ParseFile() {

		try {

			// Parse the file and use one intput line at a time
			FileInputStream fstream = new FileInputStream(Constants.FILEPATH
					+ Constants.INPUTFILENAME);
			// Get the object of DataInputStream
			DataInputStream in = new DataInputStream(fstream);
			BufferedReader br = new BufferedReader(new InputStreamReader(in));
			String strLine;
			// Read File Line By Line
			while ((strLine = br.readLine()) != null) {
				// Call the function to handle each line of the input
				handleOperations(strLine);
			}
			// Close the input stream
			in.close();

		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	/**
	 * This function receives the line read by the parse file function and determines 
	 * the appropriate function call based on the type of operation encountered
	 * Before the specific function call is made the corresponding inputLine object is initialized and sent  
	 * @param inputLine
	 */
	public void handleOperations(String inputLine) {
		TransactionOperations txnOperationDelegator = new TransactionOperations();
		SystemOperations sysOperationDelegator = new SystemOperations();

		try {
			inputLine = inputLine.replaceAll("\\s","");
			String ithOperation = inputLine;			
			System.out.println(inputLine);
			Output output = new Output();
			output.outputDetailsToFile(ithOperation);

			if (ithOperation.startsWith(Constants.BEGIN)) {
				Operation op = new Operation();
				op.setOperationName(Constants.BEGIN);
				op.setOperationType(Constants.TRANSACTIONAL);

				Transaction tr = new Transaction();
				tr.setStatus(Constants.ACTIVE);
				tr.setTransactionId(Integer.valueOf(Character
						.toString(ithOperation.charAt(1))));

				InputLine inputLineObj = new InputLine();
				inputLineObj.setItem(null);
				inputLineObj.setOperation(op);
				inputLineObj.setTransaction(tr);

				// Delegate the operation to Begin method
				txnOperationDelegator.beginOperation(inputLineObj);

			} else if (ithOperation.startsWith(Constants.END)) {
				System.out.println("end");

				Operation op = new Operation();
				op.setOperationName(Constants.END);
				op.setOperationType(Constants.TRANSACTIONAL);

				Transaction tr = new Transaction();
				tr.setStatus(Constants.COMMITTED);
				tr.setTransactionId(Integer.valueOf(Character
						.toString(ithOperation.charAt(1))));

				InputLine inputLineObj = new InputLine();
				inputLineObj.setItem(null);
				inputLineObj.setOperation(op);
				inputLineObj.setTransaction(tr);

				// Delegate the operation to end method
				txnOperationDelegator.endOpration(inputLineObj);

			} else if (ithOperation.startsWith(Constants.READ)) {

				Operation op = new Operation();
				op.setOperationName(Constants.READ);
				op.setOperationType(Constants.TRANSACTIONAL);

				Item itm = new Item();
				itm.setItemName(Character.toString(ithOperation.charAt(3)));

				Transaction tr = new Transaction();
				tr.setTransactionId(Integer.valueOf(Character
						.toString(ithOperation.charAt(1))));
				tr.setStatus(Constants.ACTIVE);

				InputLine inputLineObj = new InputLine();
				inputLineObj.setItem(itm);
				inputLineObj.setOperation(op);
				inputLineObj.setTransaction(tr);

				txnOperationDelegator.readOperation(inputLineObj);

			} else if (ithOperation.startsWith(Constants.WRITE)) {

				Operation op = new Operation();
				op.setOperationName(Constants.WRITE);
				op.setOperationType(Constants.TRANSACTIONAL);

				Item itm = new Item();
				itm.setItemName(Character.toString(ithOperation.charAt(3)));
				itm.setItemBFIM(Integer.valueOf(Character.toString(ithOperation
						.charAt(7))));
				itm.setItemAFIM(Integer.valueOf(Character.toString(ithOperation
						.charAt(5))));

				// adding into the transaction table
				Transaction transaction = Initialize.transactionTable
						.get(Integer.valueOf(Character.toString(ithOperation
								.charAt(1))));
				if (transaction != null) {
					transaction.setTransactionId(Integer.valueOf(Character
							.toString(ithOperation.charAt(1))));
					transaction.setStatus(Constants.ACTIVE);
					List<Item> itemList = null;

					if (transaction.getModifiedItems() != null) {
						itemList = transaction.getModifiedItems();
					} else {
						itemList = new ArrayList<Item>();
					}

					itemList.add(itm);
					transaction.setModifiedItems(itemList);
				} else if (transaction == null) {
					transaction = new Transaction();
					transaction.setTransactionId(Integer.valueOf(Character
							.toString(ithOperation.charAt(1))));
					transaction.setStatus(Constants.ACTIVE);
					List<Item> itemList = new ArrayList<Item>();
					itemList.add(itm);
					transaction.setModifiedItems(itemList);
				}
				Initialize.transactionTable.put(transaction.getTransactionId(),
						transaction);

				InputLine inputLineObj = new InputLine();
				inputLineObj.setItem(itm);
				inputLineObj.setOperation(op);
				inputLineObj.setTransaction(transaction);

				txnOperationDelegator.writeOperation(inputLineObj);
			} else if (ithOperation.startsWith(Constants.COMMIT)) {

				Operation op = new Operation();
				op.setOperationName(Constants.COMMIT);
				op.setOperationType(Constants.TRANSACTIONAL);

				Transaction tr = new Transaction();
				tr.setStatus(Constants.COMMITTED);
				tr.setTransactionId(Integer.valueOf(Character
						.toString(ithOperation.charAt(1))));

				InputLine inputLineObj = new InputLine();
				inputLineObj.setItem(null);
				inputLineObj.setOperation(op);
				inputLineObj.setTransaction(tr);

				// Delegate the operation to end method
				txnOperationDelegator.commitOperation(inputLineObj);

			} else if (ithOperation.startsWith(Constants.ABORT)) {

				Operation op = new Operation();
				op.setOperationName(Constants.ABORT);
				op.setOperationType(Constants.TRANSACTIONAL);

				Transaction tr = Initialize.transactionTable.get(Integer
						.valueOf(Character.toString(ithOperation.charAt(1))));
				tr.setStatus(Constants.ABORTED);

				InputLine inputLineObj = new InputLine();
				inputLineObj.setItem(null);
				inputLineObj.setOperation(op);
				inputLineObj.setTransaction(tr);

				// Delegate the operation to end method
				txnOperationDelegator.abortOperation(inputLineObj);

			} else if (ithOperation.startsWith(Constants.CHECKPOINT)) {				

				InputLine inputLineObj = new InputLine();

				Operation op = new Operation();
				op.setOperationName(Constants.CHECKPOINT);
				op.setOperationType(Constants.SYSTEM);

				inputLineObj.setItem(null);
				inputLineObj.setOperation(op);
				inputLineObj.setTransaction(null);

				sysOperationDelegator.createCheckpoint(inputLineObj);
			} else if (ithOperation.startsWith(Constants.WRITE_I)) {
				
				InputLine inputLineObj = new InputLine();

				Operation op = new Operation();
				op.setOperationName(Constants.WRITE_I);
				op.setOperationType(Constants.SYSTEM);

				inputLineObj.setItem(null);
				inputLineObj.setOperation(op);
				inputLineObj.setTransaction(null);

				sysOperationDelegator.WriteOfI(inputLineObj, Integer
						.valueOf(Character.toString(ithOperation.charAt(2))));
				//DataBuffer dbuf = new DataBuffer();

			} else if (ithOperation.startsWith(Constants.FORCE_WRITE)) {
				
				InputLine inputLineObj = new InputLine();

				Operation op = new Operation();
				op.setOperationName(Constants.FORCE_WRITE);
				op.setOperationType(Constants.SYSTEM);

				inputLineObj.setItem(null);
				inputLineObj.setOperation(op);
				inputLineObj.setTransaction(null);

				sysOperationDelegator.forceWrite(inputLineObj);

				// TODO update log buffer
			} else if (ithOperation.startsWith(Constants.FAILURE)) {

				InputLine inputLineObj = new InputLine();

				Operation op = new Operation();
				op.setOperationName(Constants.FAILURE);
				op.setOperationType(Constants.SYSTEM);

				inputLineObj.setItem(null);
				inputLineObj.setOperation(op);
				inputLineObj.setTransaction(null);

				sysOperationDelegator.failure(inputLineObj);
			}

		} catch (Exception e) {
			System.out.println("EXCEPTION IN OPERATION DELEGATOR CLASS");
			e.printStackTrace();
		}
	}
}
